//rgArray.h

#ifndef _RG_ARRAY_H_
#define _RG_ARRAY_H_

#include "rgDefines.h"

#define RG_ARRAY_DEFAULT_SIZE		64
#define RG_ARRAY_DEFAULT_GROWTH		32
#define RG_ARRAY_ERR				-1

/*! rgBasicArray: A template for static memory arrays
*/
//NOTE: Must be template<type BaseType> because it's using memcopy
template<class BaseType>
class rgBasicArray
{
private:
	rgInt		m_size;
	BaseType*	m_pData;
public:
	rgBasicArray(rgInt size=RG_ARRAY_DEFAULT_SIZE){ //?? long
		ASSERT(size>=0);
		m_size = size;
		if(size>0) {
			m_pData = (BaseType *) malloc(m_size*sizeof(BaseType));
			//NOTE: The use of malloc/free with new/delete corrupts the
			//		memory in windows. BAD IDEA :(
			//:( m_pData = new BaseType[m_size];
			ASSERT(m_pData);
		} else {
			m_pData = NULL;
		}
	};

	rgBasicArray(const rgBasicArray<BaseType>& aref) {
		m_pData=NULL;
		copy(aref);
	}

	~rgBasicArray(void){
		if(m_pData) {
			free(m_pData);
			//:( delete [] m_pData;
		}
	};

	inline BaseType& operator [] (rgInt index) {
		ASSERT((index>=0 && index<m_size));
		return m_pData[index];
	};

	inline rgInt getSize(void) { return m_size; };
	
	//inline BaseType* getPtr(void) { return m_pData; };

	virtual void copy(const rgBasicArray<BaseType>& aref) {
		//if(m_pData) {
		//	m_size = 0;
		//	free(m_pData);
		//	//delete [] m_pData;
		//	m_pData = NULL;
		//}
		memFree();

		m_size = aref.m_size;
		m_pData = (BaseType *) malloc(m_size*sizeof(BaseType));
		//:( m_pData = new BaseType[m_size];
		memcpy(m_pData,aref.m_pData,m_size);
		//NOTE: Implemented this way because a class must not be memcopied...
		//for(register rgInt i=0;i<m_size;i++){
		//	m_pData[i] = aref.m_pData[i];
		//}
	}

	BaseType* detach(void) {
		BaseType *pret = m_pData;
		m_size = 0;
		m_pData = NULL;
		return pret;
	};

	void atach(BaseType *pData, rgInt size) {
		m_pData = pData;
		m_size = size;
	}

	inline void memFree(void) {
		if(m_pData) {
			m_size = 0;
			free(m_pData);
			//:( delete [] m_pData;
			m_pData = NULL;
		}
	}

	void memAlloc( rgInt size ) {
		ASSERT(size>=0);
		m_size = size;
		if(m_pData) {
			free(m_pData);
			//:( delete [] m_pData;
		}
		m_pData = (BaseType *) malloc(m_size*sizeof(BaseType));
		//:( m_pData = new BaseType[m_size];
		ASSERT(m_pData);
	};
};

/*! rgDynArray: A template for dynamic memory arrays
*/
//NOTE: Must be template<type BaseType> because it's using memcopy
template<class BaseType>
class rgDynArray
{
private:
	rgInt		m_growth;
	rgInt		m_count;
	rgInt		m_size;
	BaseType*	m_pData;

	inline rgInt Grow(rgInt size=-1) {
		rgInt growth = m_growth;

		if(size>0) growth = size; //NOTE: Could be growth += size;
		m_size+=growth;
		if(m_count==0) {
			if(m_pData) {
				free(m_pData);
				//:( delete [] m_pData;
			}
			m_pData = (BaseType *) malloc(m_size*sizeof(BaseType));
			//:( m_pData = new BaseType[m_size];
		} else {
			ASSERT(m_pData);
			m_pData	= (BaseType *) realloc(m_pData, m_size*sizeof(BaseType));
			//:( m_pData = new BaseType[m_size];
		}
		if(!m_pData) return RG_ARRAY_ERR;
		return 0;
	}
public:
	rgDynArray(rgInt growth=RG_ARRAY_DEFAULT_GROWTH){
		ASSERT(growth>=0);
		m_count = 0;
		m_size = m_growth = growth;
		if(m_size>0) {
			m_pData = (BaseType *) malloc(m_size*sizeof(BaseType));
			//:( m_pData = new BaseType[m_size];
			ASSERT(m_pData);
		} else {
			m_pData = NULL;
		}
	};

	rgDynArray(const rgDynArray<BaseType>& aref) {
		m_pData=NULL;
		copy(aref);
	}

	~rgDynArray(void){
		if(m_pData) {
			free(m_pData);
			//:( delete [] m_pData;
		}
	};

	inline BaseType& operator [] (rgInt index) {
		ASSERT((index>=0 && index<m_count));
		return m_pData[index];
	};

	inline rgInt getAllocSize(void) { return m_size; };
	
	inline rgInt getCount(void) { return m_count; };
	
	//inline BaseType* getPtr(void) { return m_pData; };
	
	inline void clear(void) { m_count=0; };

	inline void setGrowth( rgInt growth ) {
		ASSERT(growth>0);
		m_growth = growth;
	}

	rgInt add(const BaseType& ref) {
		rgInt index = m_count;

		if(m_size<=m_count) {
			if(Grow()) {
				return RG_ARRAY_ERR;
			}
		}
		m_pData[m_count] = ref;
		m_count++;
		return index;
	}

	rgInt add(const BaseType* pRefs, rgInt size) {

		if(m_size<=(m_count + size)) {
			if(Grow()) {
				return RG_ARRAY_ERR;
			}
		}
		memcpy(&m_pData[m_count], pRefs, size*sizeof(BaseType));
		//NOTE: Implemented this way because a class must not be memcopied...
		//for(register rgInt i = 0; i<size; i++) {
		//	m_pData[m_count+i] = pRefs[i];
		//}
		m_count+=size;
		return 0;
	}

	rgInt insert(const BaseType& ref, rgInt atPos) {
		ASSERT(atPos>=0);
		if(atPos>m_count) atPos=m_count;
		if(m_count==m_size) {
			if(Grow()) 
				return RG_ARRAY_ERR;
		}
		if(atPos!=m_count)
			memcpy(&m_pData[atPos+1],&m_pData[atPos],(m_count-atPos)*sizeof(BaseType));
		m_pData[atPos] = ref;
		m_count++;
		return 0;
	}
	
	rgInt insert(const BaseType* pRefs, rgInt size, rgInt atPos) {
		ASSERT(atPos>=0);
		if(atPos>m_count) atPos=m_count;
		if((m_count+size)>=m_size){
			if(Grow(size))
				return RG_ARRAY_ERR;
		}
		if(atPos!=m_count)
			memcpy(&m_pData[atPos+size],m_pData[atPos],(m_count-atPos)*sizeof(BaseType));
		//NOTE: Implemented this way because a class must not be memcopied...
		//for(register rgInt i = 0; i<size; i++) {
		//	m_pData[m_count+i] = pRefs[i];
		//}
		memcpy(&m_pData[atPos], pRefs, size*sizeof(BaseType));
		m_count+=size;
	}

	inline void remove(rgInt count = 1) {
		m_count-=count;
	}

	void removeAt(rgInt atPos, rgInt count = 1) {
		ASSERT((atPos>=0 && count>0));
		if( (atPos+count)>=m_count ) {
			m_count = atPos;
		} else {
			memcpy(&m_pData[atPos],&m_pData[(atPos+count)],
				(m_count-count-atPos)*sizeof(BaseType));
			m_count-=count;
		}
	}

	virtual void copy(const rgDynArray<BaseType>& aref) {
	//	if(m_pData) {
	//		m_size = 0;
	//		delete [] m_pData;
	//		m_pData = NULL;
	//	}
		memFree();

		m_size = aref.m_size;
		m_count = aref.m_count;
		m_growth = aref.m_growth;
		m_pData = (BaseType *) malloc(m_size * sizeof(BaseType));
	//:( m_pData = new BaseType[m_size];
		memcpy(m_pData,aref.m_pData,m_count*sizeof(BaseType));
	//NOTE: Implemented this way because a class must not be memcopied...
	//	for(i=0;i<m_size;i++){
	//		m_pData[i] = aref.m_pData[i];
	//	}
	}

	inline void memFree(void) {
		if(m_pData) {
			m_count = m_size = 0;
			free(m_pData);
			//:( delete [] m_pData;
			m_pData = NULL;
		}
	}

	void memAlloc( rgInt size ) {
		ASSERT(size>=m_count);
		if(size > m_size) {
			m_size = size;
			if(m_pData) {
				m_pData = (BaseType *) realloc(m_pData, m_size * sizeof(BaseType));
			} else {
				m_pData = (BaseType *) malloc(m_size * sizeof(BaseType));
			}
			ASSERT(m_pData);
		}
	};

	void memShrink( void ) {
		if(m_count < m_size ) {
			m_size = m_count;
			//NOTE: If the realloc reserves less memory then is there is memory leaks?
			m_pData = (BaseType *) realloc(m_pData, m_size * sizeof(BaseType));
			ASSERT((m_pData || m_size==0));
		}
	};
};

#endif //_RG_ARRAY_H_
